fix(auth): log in when the browser is not on this machine - #1418
fix(auth): log in when the browser is not on this machine#1418cristifalcas wants to merge 3 commits into
Conversation
`aspect auth login` opened the browser by shelling out to `open`, then waited
for the OAuth code on a loopback listener. Both halves fail away from a desktop:
- `open` is macOS. On Linux it is at best Debian's xdg-utils symlink, so a
login either recited `www-browser: not found` six times or died with
"failed to spawn command open" and an AXL traceback (ENG-2098).
- Worse, the redirect is a loopback address. Paste the URL into a browser on
another machine and the code is delivered to *that* machine's port —
ERR_CONNECTION_REFUSED for the user and a CLI that waits forever. No amount
of browser-opening fixes it; over SSH the code has to come back by hand.
Opening a browser moves into the runtime as `ctx.aspect.auth.open_browser`,
which carries the platform table (`$BROWSER`, then xdg-open/gio/gnome-open/
kde-open/x-www-browser/wslview on Linux, `open` on macOS, rundll32 on Windows —
it takes the URL as one argument, where `cmd /c start` would hand an
`&`-separated authorize URL back to cmd.exe to re-parse). Launcher output is
discarded; the task prints one message that says what to do instead.
It reports "headless" without trying at all when this is an SSH session with no
forwarded display. A display counts only if it names a host (`localhost:10.0`,
what ssh -X sets) — a bare `:0` is the remote machine's own screen, which is
what a Cloud Workstation image sets and where a browser would open in front of
nobody.
`AuthSession.redeem(pasted)` is then the login path for those sessions: it takes
the browser's full address-bar URL, a bare query string, or the bare code.
`wait()` and `redeem()` now share one `complete()`, so both reach an identical
token exchange; `state` is still validated whenever it is present, and a stale
paste from an earlier run is rejected. Off a TTY there is nothing to paste with,
so the listener is still waited on — a forwarded port delivers.
`--no-browser` forces the paste flow on both `login` and `configure`. The port
is only worth forwarding for the Aspect account, which binds a fixed 19556; a
self-hosted deployment takes a fresh port each run, so that caveat lives in the
flag's help text rather than as a hint the user cannot act on.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ef89800e5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for entry in browser_env.unwrap_or_default().split(':') { | ||
| let argv: Vec<String> = entry.split_whitespace().map(str::to_string).collect(); |
There was a problem hiding this comment.
Preserve quoting when parsing BROWSER commands
When $BROWSER contains a quoted command line such as firefox --new-window "%s" or sh -c 'open-browser %s', split_whitespace() leaves the quote characters in the arguments and splits shell expressions incorrectly. spawn_launcher then substitutes the authorize URL into those malformed arguments; if the launcher still exits successfully, the CLI reports that the browser opened and waits indefinitely even though it received an invalid URL. Parse each colon-separated entry with shell-aware argument parsing rather than raw whitespace splitting.
Useful? React with 👍 / 👎.
Aspect Workflows Tasks📅 Thu Sep 3 19:47:31 UTC 2026 Task Results
⏱ Last updated Thu Sep 3 19:58:09 UTC 2026 · 📊 GitHub API quota 2,142/15,000 (14% used, resets in 23m) |
Fixes
aspect auth loginwhen the CLI and browser run on different machines. Closes ENG-2098.What changed
--no-browsertoaspect auth loginandaspect auth configure. The explicit flag reads the callback from stdin even when stdin is not a TTY.Behaviour
--no-browserprints the authorization URL and prompts for the callback URL or code when input is interactive or explicitly requested.For self-hosted deployments, listener callbacks require the expected OAuth
state. Pasted callback URLs validatestate; a bare authorization code may omit it and remains bound to the login session by PKCE.Test plan
bazel test //crates/axl-runtime:test //crates/aspect-cli:testbazel-bin/crates/aspect-cli/aspect-cli tests axl— 991 tests passedDISPLAY=:0, SSH with forwarded X11,--no-browser, loopback delivery, stale callback state, missing codes, and empty inputNot yet verified: a live end-to-end login with a real authorization code, and the Linux fallback launcher table on Linux.